﻿using Nuctech.ACCT.OIS.SqlSugar.Repository;
using SqlSugar;
using System.Linq.Expressions;
using Volo.Abp.Data;
using Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
using Nuctech.ACCT.OIS.SqlSugar.Util;
using System.Linq;
using System.Collections.Generic;
using System;
using System.Threading.Tasks;
using System.Threading;

namespace Nuctech.ACCT.OIS.SqlSugar.AdaptAbpRepository
{
    /// <summary>
    /// 兼容ISugarRepository, ISimpleClient<TEntity>、IRepository<TEntity>等仓储的集合体
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public class SqlSugarAdaptationAbpRepository<TEntity> : SqlSugarRepository<TEntity>, ISqlSugarRepository<TEntity> where TEntity : class, IEntity, new()
    {
        /// <summary>
        /// 目前还没实现自动切换数据库，后期要去掉这个
        /// </summary>
        //private SimpleClient<TEntity> SimpleClient { get; }

        public ITenant Tenant => Context.AsTenant();

        public ISugarQueryable<TEntity> DbQueryable => Tenant.QueryableWithAttr<TEntity>();

        private IInsertable<TEntity> DbInsertable(TEntity insertObj) => Tenant.InsertableWithAttr(insertObj);
        private IInsertable<TEntity> DbInsertable(List<TEntity> insertObjs) => Tenant.InsertableWithAttr(insertObjs);

        private IUpdateable<TEntity> DbUpdateable() => Tenant.UpdateableWithAttr<TEntity>();
        private IUpdateable<TEntity> DbUpdateable(TEntity updateObj) => Tenant.UpdateableWithAttr(updateObj);
        private IUpdateable<TEntity> DbUpdateable(List<TEntity> updateObjs) => Tenant.UpdateableWithAttr(updateObjs);

        private IDeleteable<TEntity> DbDeleteable() => Tenant.DeleteableWithAttr<TEntity>();
        private IDeleteable<TEntity> DbDeleteable(TEntity deleteObj) => Tenant.DeleteableWithAttr(deleteObj);
        private IDeleteable<TEntity> DbDeleteable(List<TEntity> deleteObjs) => Tenant.DeleteableWithAttr(deleteObjs);

        public bool? IsChangeTrackingEnabled { get; set; }

        public IAsyncQueryableExecuter AsyncExecuter => throw new NotImplementedException();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="db"></param>
        public SqlSugarAdaptationAbpRepository(ISqlSugarClient db) : base(db)
        {
            //SimpleClient = new SimpleClient<TEntity>(db);
        }

        #region IRepository 接口实现

        public async Task<TEntity?> FindAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default)
        {
            return await DbQueryable.FirstAsync(predicate);
        }

        public async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default)
        {
            return await DbQueryable.FirstAsync(predicate);
        }

        public async Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
            {
                _ = await DbUpdateable().SetColumns(nameof(ISoftDelete.IsDeleted), true).Where(predicate).ExecuteCommandAsync() > 0;
            }
            else
            {
                var flag = await DbDeleteable().Where(predicate).ExecuteCommandAsync() > 0;
            }
        }

        public async Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            return await DbQueryable.Where(predicate).ToListAsync();
        }

        public async Task<List<TEntity>> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            return await DbQueryable.ToListAsync();
        }

        public async Task<TEntity> InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            return await DbInsertable(entity).ExecuteReturnEntityAsync();
        }

        public async Task InsertManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await DbInsertable((entities.ToList())).ExecuteCommandAsync();
        }

        public async Task<TEntity> UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await SqlSugarUpdateAsync(entity);
            return entity;
        }

        public async Task UpdateManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            _ = await DbUpdateable(entities.ToList()).ExecuteCommandAsync() > 0;
        }

        public async Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await SqlSugarDeleteAsync(entity);
        }

        public async Task DeleteManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await SqlSugarDeleteAsync(entities.ToList());
        }

        public async Task<long> GetCountAsync(CancellationToken cancellationToken = default)
        {
            return await DbQueryable.CountAsync();
        }

        public async Task<List<TEntity>> GetPagedListAsync(int skipCount, int maxResultCount, string sorting, bool includeDetails = false, CancellationToken cancellationToken = default)
        {
            return await SqlSugarGetPageListAsync(_ => true, skipCount, maxResultCount);
        }

        #region 弃用

        [Obsolete("已弃用")]
        public Task<bool> AllAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            throw new NotImplementedException();
        }

        [Obsolete("用DeleteAsync")]
        public Task DeleteDirectAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
        {
            throw new NotImplementedException();
        }

        [Obsolete("已弃用")]
        public IQueryable<TEntity> WithDetails()
        {
            throw new NotImplementedException();
        }

        [Obsolete("已弃用")]
        public IQueryable<TEntity> WithDetails(params Expression<Func<TEntity, object>>[] propertySelectors)
        {
            throw new NotImplementedException();
        }

        [Obsolete("已弃用")]
        public Task<IQueryable<TEntity>> WithDetailsAsync()
        {
            throw new NotImplementedException();
        }

        [Obsolete("已弃用")]
        public Task<IQueryable<TEntity>> WithDetailsAsync(params Expression<Func<TEntity, object>>[] propertySelectors)
        {
            throw new NotImplementedException();
        }

        [Obsolete("已弃用")]
        public Task<IQueryable<TEntity>> GetQueryableAsync()
        {
            throw new NotImplementedException();
        }

        #endregion

        #endregion

        #region RepositoryAsyncExtensions扩展常用方法迁移

        public async Task<bool> ContainsAsync(TEntity item)
        {
            return await DbQueryable.Where(x => x.Equals(item)).AnyAsync();
        }

        public async Task<bool> AnyAsync()
        {
            return await DbQueryable.AnyAsync();
        }

        public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            return await DbQueryable.Where((whereExpression)).AnyAsync(); ;
        }

        public async Task<int> CountAsync()
        {
            return await DbQueryable.CountAsync();
        }

        public async Task<int> CountAsync(Expression<Func<TEntity, bool>> whereExpression, bool isExtensions = true)
        {
            return await DbQueryable.Where(whereExpression).CountAsync();
        }

        public async Task<TEntity> FirstAsync()
        {
            return await DbQueryable.FirstAsync();
        }

        public async Task<TEntity> FirstAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            return await DbQueryable.Where(whereExpression).FirstAsync();
        }

        public async Task<TEntity?> FirstOrDefaultAsync()
        {
            return await DbQueryable.FirstAsync();
        }

        public async Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            return await DbQueryable.Where(whereExpression).FirstAsync();
        }

        public async Task<TEntity> SingleAsync()
        {
            return await DbQueryable.SingleAsync();
        }

        public async Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            return await DbQueryable.Where(whereExpression).SingleAsync();
        }

        public async Task<TEntity?> SingleOrDefaultAsync()
        {
            return await DbQueryable.SingleAsync();
        }

        public async Task<TEntity?> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            return await DbQueryable.Where(whereExpression).SingleAsync();
        }

        public async Task<TResult> MinAsync<TResult>(Expression<Func<TEntity, TResult>> whereExpression)
        {
            return await DbQueryable.MinAsync(whereExpression);
        }

        public async Task<TResult> MaxAsync<TResult>(Expression<Func<TEntity, TResult>> whereExpression)
        {
            return await DbQueryable.MaxAsync(whereExpression);
        }

        public async Task<decimal> SumAsync(Expression<Func<TEntity, decimal>> whereExpression)
        {
            return await DbQueryable.SumAsync(whereExpression);
        }

        public async Task<decimal?> SumAsync(Expression<Func<TEntity, decimal?>> whereExpression)
        {
            return await DbQueryable.SumAsync(whereExpression);
        }

        public async Task<int> SumAsync(Expression<Func<TEntity, int>> whereExpression)
        {
            return await DbQueryable.SumAsync(whereExpression);
        }

        public async Task<int?> SumAsync(Expression<Func<TEntity, int?>> whereExpression)
        {
            return await DbQueryable.SumAsync(whereExpression);
        }

        public async Task<long> SumAsync(Expression<Func<TEntity, long>> whereExpression)
        {
            return await DbQueryable.SumAsync(whereExpression);
        }

        public async Task<long?> SumAsync(Expression<Func<TEntity, long?>> whereExpression)
        {
            return await DbQueryable.SumAsync(whereExpression);
        }

        public async Task<double> SumAsync(Expression<Func<TEntity, double>> whereExpression)
        {
            return await DbQueryable.SumAsync(whereExpression);
        }

        public async Task<double?> SumAsync(Expression<Func<TEntity, double?>> whereExpression)
        {
            return await DbQueryable.SumAsync(whereExpression);
        }

        public async Task<float> SumAsync(Expression<Func<TEntity, float>> whereExpression)
        {
            return await DbQueryable.SumAsync(whereExpression);
        }

        public async Task<float?> SumAsync(Expression<Func<TEntity, float?>> whereExpression)
        {
            return await DbQueryable.SumAsync(whereExpression);
        }

        public async Task<decimal> AverageAsync(Expression<Func<TEntity, decimal>> whereExpression)
        {
            return await DbQueryable.AvgAsync(whereExpression);
        }

        public async Task<decimal?> AverageAsync(Expression<Func<TEntity, decimal?>> whereExpression)
        {
            return await DbQueryable.AvgAsync(whereExpression);
        }

        public async Task<double> AverageAsync(Expression<Func<TEntity, int>> whereExpression)
        {
            return await DbQueryable.AvgAsync(whereExpression);
        }

        public async Task<double?> AverageAsync(Expression<Func<TEntity, int?>> whereExpression)
        {
            return await DbQueryable.AvgAsync(whereExpression);
        }

        public async Task<double> AverageAsync(Expression<Func<TEntity, long>> whereExpression)
        {
            return await DbQueryable.AvgAsync(whereExpression);
        }

        public async Task<double?> AverageAsync(Expression<Func<TEntity, long?>> whereExpression)
        {
            return await DbQueryable.AvgAsync(whereExpression);
        }

        public async Task<double> AverageAsync(Expression<Func<TEntity, double>> whereExpression)
        {
            return await DbQueryable.AvgAsync(whereExpression);
        }

        public async Task<double?> AverageAsync(Expression<Func<TEntity, double?>> whereExpression)
        {
            return await DbQueryable.AvgAsync(whereExpression);
        }

        public async Task<float?> AverageAsync(Expression<Func<TEntity, float?>> whereExpression)
        {
            return await DbQueryable.AvgAsync(whereExpression);
        }

        public async Task<List<TEntity>> ToListAsync()
        {
            return await DbQueryable.ToListAsync();
        }

        public async Task<TEntity[]> ToArrayAsync()
        {
            return await DbQueryable.ToArrayAsync();
        }


        #endregion

        #region private


        private async Task<bool> SqlSugarDeleteAsync(TEntity deleteObj)
        {
            if (deleteObj is ISoftDelete)
            {
                ReflexHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, deleteObj);
                return await SqlSugarUpdateAsync(deleteObj);
            }
            else
            {
                return await DbDeleteable(deleteObj).ExecuteCommandAsync() > 0;
            }
        }

        private async Task<bool> SqlSugarDeleteAsync(List<TEntity> deleteObjs)
        {
            if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
            {
                deleteObjs.ForEach(e => ReflexHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, e));
                return await DbUpdateable(deleteObjs).ExecuteCommandAsync() > 0;
            }
            else
            {
                return await DbDeleteable().Where(deleteObjs).ExecuteCommandAsync() > 0;
            }
        }

        private async Task<bool> SqlSugarDeleteAsync(Expression<Func<TEntity, bool>> whereExpression)
        {
            if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
            {
                return await DbUpdateable().SetColumns(nameof(ISoftDelete.IsDeleted), true).Where(whereExpression).ExecuteCommandAsync() > 0;
            }
            else
            {
                return await DbDeleteable().Where(whereExpression).ExecuteCommandAsync() > 0;
            }

        }

        private async Task<List<TEntity>> SqlSugarGetPageListAsync(Expression<Func<TEntity, bool>> whereExpression, int pageNum, int pageSize)
        {
            var page = new PageModel() { PageIndex = pageNum, PageSize = pageSize };
            RefAsync<int> count = 0;
            List<TEntity> result = await DbQueryable.Where(whereExpression).ToPageListAsync(page.PageIndex, page.PageSize, count);
            page.TotalCount = count;
            return result;
        }

        private async Task<bool> SqlSugarUpdateAsync(TEntity updateObj)
        {
            if (typeof(TEntity).IsAssignableTo<IHasConcurrencyStamp>())//带版本号乐观锁更新
            {
                try
                {
                    int num = await DbUpdateable(updateObj).ExecuteCommandWithOptLockAsync(true);
                    return num > 0;
                }
                catch (VersionExceptions ex)
                {

                    throw new AbpDbConcurrencyException($"{ex.Message}[更新失败：ConcurrencyStamp不是最新版本],entityInfo：{updateObj}", ex);
                }
            }

            return await DbUpdateable(updateObj).ExecuteCommandAsync() > 0;
        }

        #endregion
    }

    public class SqlSugarAdaptationAbpRepository<TEntity, TKey> : SqlSugarAdaptationAbpRepository<TEntity>, ISqlSugarRepository<TEntity, TKey>, IRepository<TEntity, TKey> where TEntity : class, IEntity<TKey>, new()
    {
        public SqlSugarAdaptationAbpRepository(ISqlSugarClient db) : base(db)
        {

        }

        #region IBasicRepository接口方法

        public async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await DeleteByIdAsync(id);
        }

        public async Task DeleteManyAsync(IEnumerable<TKey> ids, bool autoSave = false, CancellationToken cancellationToken = default)
        {
            await DeleteByIdsAsync(ids.Select(x => (object)x).ToArray());
        }

        public async Task<TEntity> GetAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
        {
            return await GetByIdAsync(id);
        }

        public async Task<TEntity?> FindAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default)
        {
            return await GetByIdAsync(id);
        }

        #endregion
    }
}